﻿using LambdaToSql.FrameWork;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Linq.Expressions;
using Newtonsoft.Json;
using System.Diagnostics;

namespace LambdaToSql.Realization
{
    internal class Insertable<T> : LambdaToSql.Interface.IInsertable<T> where T : class, new()
    {
        public virtual LambdaToSql.EntityModel.DbContext Context { get; set; }

        internal LambdaToSql.EntityModel.InsertEntity<T> Entity { get; set; }

        public Insertable(LambdaToSql.EntityModel.DbContext context)
        {
            this.Context = context;
            //Entity = new LambdaToSql.EntityModel.InsertEntity<T>(this.Context);

            string assemblyName = "LambdaToSql";
            string fullTypeName = string.Format("LambdaToSql.EntityModel.{0}.{1}`1", this.Context.SqlType.ToString(), "InsertEntity");
            Type objType = Assembly.Load(assemblyName).GetType(fullTypeName);
            Type type = objType.MakeGenericType(typeof(T));
            Entity = (LambdaToSql.EntityModel.InsertEntity<T>)Activator.CreateInstance(type, new object[] { this.Context });
        }

        public virtual LambdaToSql.Interface.IInsertable<T> Insert(T obj)
        {
            this.Entity.TableName = this.Entity.TableName;
            this.Entity.Obj = obj;

            return this;
        }

        public virtual LambdaToSql.Interface.IInsertable<T> InsertColumns(Expression<Func<T, object>> exp)
        {
            if (exp != null)
            {
                var obj = new LambdaRouter(this.Entity.Parameters);
                string str = obj.ExpressionRouter(exp);
                this.Entity.InsertColumns.AddRange(str.Split(',').ToList());
            }
            return this;
        }

        public virtual LambdaToSql.Interface.IInsertable<T> IgnoreColumns(Expression<Func<T, object>> exp)
        {
            if (exp != null)
            {
                var obj = new LambdaRouter(this.Entity.Parameters);
                string str = obj.ExpressionRouter(exp);
                this.Entity.IgnoreFields.AddRange(str.Split(',').ToList());
            }
            return this;
        }


        public virtual string GetSql(out object retValue)
        {
            var DbHelper = this.Context.DbHelper;

            var properties = this.Entity.Properties.ToList();
            if (this.Entity.InsertColumns.Count > 0)
            {
                properties = properties.Where(ex => this.Entity.InsertColumns.Contains(ex.Name)).ToList();
            }
            if (this.Entity.IgnoreFields.Count > 0)
            {
                properties = properties.Where(ex => !this.Entity.IgnoreFields.Contains(ex.Name)).ToList();
            }

            retValue = "";
            foreach (PropertyInfo pi in properties)
            {
                try
                {
                    var name = pi.Name;
                    var value = pi.GetValue(this.Entity.Obj, null);
                    if (value == null)
                    {
                        value = DBNull.Value;
                    }

                    var tableColumns = this.Context.Table[this.Entity.TableName].Columns;
                    var column = tableColumns.Where(ex => ex.Name == name).FirstOrDefault();

                    if (column.IsIdentity == false && column.IsPrimaryKey)//非自增列 并且树主键列=>Guid主键列
                    {
                        if (pi.PropertyType == typeof(System.Guid) || pi.PropertyType == typeof(System.Guid?))
                        {
                            if (value == DBNull.Value || value.ToString() == Guid.Empty.ToString())
                            {
                                value = System.Guid.NewGuid();
                            }
                            retValue = value;
                        }
                    }
                    if (value != DBNull.Value && column.IsIdentity == false)//非自增列
                    {
                        var ParaName = "@para" + (this.Entity.Parameters.Count);
                        this.Entity.Fields.Add(name.ToString());
                        this.Entity.AddFields.Add(ParaName);
                        this.Entity.Parameters.Add(new LambdaToSql.EntityModel.DataParameter(ParaName, value));
                    }
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
            var sql = string.Format(this.Entity.InsertTemplate, string.Join(",", this.Entity.Fields), string.Join(",", this.Entity.AddFields));
            return sql;
        }

        public virtual string ExecuteNonQuery()
        {
            var DbHelper = this.Context.DbHelper;

            object retValue = "";
            var sql = this.GetSql(out retValue);
            sql += ";SELECT @@IDENTITY AS aliasName;";

            var ret = DbHelper.ExecuteScalar(sql, this.Entity.Parameters.ToArray());
            if (!string.IsNullOrEmpty(ret))
            {
                retValue = ret;
            }
            return retValue.ToString();
        }

        public virtual LambdaToSql.EntityModel.Result<string> ExecuteNonQuery(bool debug)
        {
            var sw = new Stopwatch();
            sw.Start();

            var i = this.ExecuteNonQuery();

            var runTime = sw.ElapsedMilliseconds;
            sw.Stop();

            var sql = string.Format(this.Entity.InsertTemplate, string.Join(",", this.Entity.Fields), string.Join(",", this.Entity.AddFields));
            var values = JsonConvert.SerializeObject(this.Entity.Parameters.Select(ex => new { ex.Value, ex.Name }).ToList());
            var result = new LambdaToSql.EntityModel.Result<string>()
            {
                Data = i,
                ResultValue = i,
                Method = "ExecuteNonQuery",
                RunTime = (int)runTime,
                DeBug = string.Format("执行语句:{0}<br />值:{1}<br />执行时间:{2}ms<br />执行结果:{3}", sql, values, runTime, i)
            };
            return result;
        }
    }
}
